#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AI虚拟主播系统 - 一键启动脚本
自动启动所有必要组件并确保正确的启动顺序
"""

import subprocess
import time
import sys
import os
import webbrowser
import socket
import http.server
import socketserver
import threading
from pathlib import Path
from typing import List, Optional, Tuple, Any


class ProcessManager:
    """进程管理器"""
    
    def __init__(self):
        self.processes: List[Tuple[Any, str]] = []
        self.http_server = None
        self.http_server_thread = None
        
    def add_process(self, process: Any, name: str = ""):
        """添加进程到管理列表"""
        self.processes.append((process, name))
    
    def set_http_server(self, server, thread):
        """设置HTTP服务器引用"""
        self.http_server = server
        self.http_server_thread = thread
        
    def terminate_all(self):
        """终止所有进程"""
        print("\n正在停止所有服务...")
        
        # 先停止HTTP服务器
        if self.http_server:
            try:
                print("✓ HTTP服务器已停止")
                self.http_server.shutdown()
            except Exception as e:
                print(f"✗ 停止HTTP服务器失败: {e}")
        
        # 再停止其他进程
        for process, name in reversed(self.processes):
            try:
                process.terminate()
                process.wait(timeout=5)
                if name:
                    print(f"✓ {name} 已停止 (PID: {process.pid})")
            except subprocess.TimeoutExpired:
                process.kill()
                if name:
                    print(f"✗ {name} 强制终止 (PID: {process.pid})")
            except Exception as e:
                if name:
                    print(f"✗ 停止 {name} 失败: {e}")


def is_port_available(port: int) -> bool:
    """检查端口是否可用"""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        try:
            s.bind(('localhost', port))
            return True
        except OSError:
            return False


def kill_process_on_port(port: int) -> bool:
    """
    终止占用指定端口的进程
    
    Args:
        port: 端口号
        
    Returns:
        是否成功终止
    """
    import platform
    
    try:
        if platform.system() == 'Windows':
            # Windows: 使用 netstat 查找 PID 然后 taskkill
            import subprocess
            result = subprocess.run(
                f'netstat -ano | findstr :{port}',
                shell=True,
                capture_output=True,
                text=True
            )
            
            if result.stdout:
                # 解析输出获取 PID
                lines = result.stdout.strip().split('\n')
                pids = set()
                for line in lines:
                    parts = line.split()
                    if len(parts) >= 5 and 'LISTENING' in line:
                        pid = parts[-1]
                        if pid.isdigit():
                            pids.add(pid)
                
                if not pids:
                    print(f"  ℹ️  端口 {port} 未找到监听进程")
                    return True
                
                # 终止所有相关进程
                for pid in pids:
                    result = subprocess.run(f'taskkill /F /PID {pid}', shell=True, capture_output=True, text=True)
                    if result.returncode == 0:
                        print(f"  ✓ 已终止占用端口 {port} 的进程 (PID: {pid})")
                    else:
                        print(f"  ✗ 无法终止进程 {pid}: {result.stderr}")
                
                # 等待端口释放
                time.sleep(2)
                
                # 验证端口是否真的释放
                if is_port_available(port):
                    print(f"  ✓ 端口 {port} 已成功释放")
                    return True
                else:
                    print(f"  ✗ 端口 {port} 仍然被占用")
                    return False
            else:
                # 没有找到占用端口的进程，可能端口已经释放
                return True
        else:
            # Linux/Mac: 使用 lsof
            import subprocess
            subprocess.run(f'lsof -ti:{port} | xargs kill -9', shell=True, capture_output=True)
            time.sleep(2)
            return is_port_available(port)
            
    except Exception as e:
        print(f"  ⚠️ 终止进程失败: {e}")
        import traceback
        traceback.print_exc()
        return False
    
    return False


def wait_for_port(port: int, timeout: int = 30) -> bool:
    """等待端口开放"""
    start_time = time.time()
    while time.time() - start_time < timeout:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            try:
                s.connect(('localhost', port))
                return True
            except (ConnectionRefusedError, OSError):
                time.sleep(0.5)
    return False


def start_http_server(manager: ProcessManager) -> bool:
    """启动 HTTP 服务器（内置方式）"""
    print("\n[1/3] 启动 HTTP 服务器...")
    
    # 检查端口 8000
    max_retries = 3
    for attempt in range(max_retries):
        if is_port_available(8000):
            print("  ✓ 端口 8000 可用")
            break
        else:
            print(f"  ⚠️  端口 8000 已被占用（尝试 {attempt + 1}/{max_retries}）")
            if attempt < max_retries - 1:
                print("  正在尝试自动清理...")
                if kill_process_on_port(8000):
                    continue
                else:
                    print("  ✗ 自动清理失败，重试中...")
                    time.sleep(1)
            else:
                print("  ✗ 端口 8000 无法释放")
                print("  请手动终止占用该端口的进程：")
                if sys.platform == 'win32':
                    os.system('netstat -ano | findstr :8000')
                return False
    
    # 切换到项目根目录
    project_root = Path(__file__).parent
    os.chdir(project_root)
    print(f"  ✓ 工作目录: {project_root}")
    
    # 创建CORS支持的请求处理器
    Handler = http.server.SimpleHTTPRequestHandler
    
    class CORSRequestHandler(Handler):
        def end_headers(self):
            self.send_header('Access-Control-Allow-Origin', '*')
            self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
            self.send_header('Access-Control-Allow-Headers', 'Content-Type')
            self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate')
            super().end_headers()
        
        def log_message(self, format, *args):
            # 简化日志输出，避免干扰
            pass
    
    try:
        # 创建TCP服务器
        print("  正在创建 HTTP 服务器...", end="", flush=True)
        httpd = socketserver.TCPServer(("", 8000), CORSRequestHandler, bind_and_activate=False)
        httpd.allow_reuse_address = True
        httpd.server_bind()
        httpd.server_activate()
        print(" ✓")
        
        # 在后台线程运行
        print("  正在启动后台线程...", end="", flush=True)
        server_thread = threading.Thread(target=httpd.serve_forever, daemon=True)
        server_thread.start()
        print(" ✓")
        
        # 保存服务器引用
        manager.set_http_server(httpd, server_thread)
        
        # 验证服务器是否启动
        print("  正在验证服务器状态...", end="", flush=True)
        time.sleep(1)
        
        if wait_for_port(8000, timeout=5):
            print(" ✓")
            print("  ✓ HTTP 服务器已启动（内置）")
            print("  ✓ 地址: http://localhost:8000")
            return True
        else:
            print(" ✗")
            print("  ✗ HTTP 服务器验证失败")
            httpd.shutdown()
            return False
            
    except OSError as e:
        print(" ✗")
        if e.errno == 10048:  # 端口被占用
            print(f"  ✗ 端口8000仍然被占用")
        else:
            print(f"  ✗ OSError: {e}")
        import traceback
        traceback.print_exc()
        return False
    except Exception as e:
        print(" ✗")
        print(f"  ✗ 启动HTTP服务器异常: {e}")
        import traceback
        traceback.print_exc()
        return False


def start_main_program(manager: ProcessManager) -> Optional[subprocess.Popen]:
    """启动主程序"""
    print("\n[2/3] 启动主程序...")
    
    # 检查 WebSocket 端口 8765
    if not is_port_available(8765):
        print("⚠️  端口 8765 已被占用")
        print("正在尝试自动清理...")
        
        if kill_process_on_port(8765):
            print("✓ 端口 8765 已清理")
        else:
            print("⚠️  无法清理端口 8765，尝试继续...")
    
    main_script = Path(__file__).parent / "main.py"
    
    # 在当前终端运行主程序
    process = subprocess.Popen(
        [sys.executable, str(main_script)],
        stdout=sys.stdout,
        stderr=sys.stderr
    )
    
    print(f"  ✓ 主程序已启动 (PID: {process.pid})")
    
    # 等待 WebSocket 服务器启动
    print("  正在等待 WebSocket 服务器启动...", end="", flush=True)
    if wait_for_port(8765, timeout=20):  # 增加超时时间
        print(" ✓")
        print(f"  ✓ WebSocket 服务器已就绪 (ws://localhost:8765)")
        manager.add_process(process, "主程序")
        return process
    else:
        print(" ⚠️")
        print("  ⚠️  WebSocket 服务器启动较慢，但主程序正在运行")
        print("  ⚠️  Live2D 页面可能需要稍后才能连接")
        manager.add_process(process, "主程序")
        return process


def open_live2d_page():
    """打开 Live2D 渲染页面"""
    print("\n[3/3] 打开 Live2D 渲染页面...")
    
    # 等待一切就绪，确保 WebSocket 服务器完全启动
    print("  正在等待所有服务就绪...", end="", flush=True)
    time.sleep(3)  # 增加等待时间
    
    # 再次验证 WebSocket 端口
    if not wait_for_port(8765, timeout=5):
        print(" ⚠️")
        print("  ⚠️  WebSocket 服务器仍未就绪，Live2D 页面可能无法连接")
        print("  提示: 请稍后手动刷新页面")
    else:
        print(" ✓")
    
    url = "http://localhost:8000/web_live2d_cubism4.html"
    print(f"  正在打开: {url}")
    
    try:
        webbrowser.open(url)
        print("  ✓ Live2D 页面已在浏览器中打开")
        print("  ℹ️  如果未自动打开，请手动访问上述地址")
        return True
    except Exception as e:
        print(f"  ✗ 打开浏览器失败: {e}")
        print(f"  请手动打开: {url}")
        return False


def print_banner():
    """打印启动横幅"""
    print("=" * 70)
    print(" " * 15 + "🎤 AI虚拟主播系统 - 一键启动")
    print("=" * 70)
    print()


def print_startup_info():
    """打印启动信息"""
    print("\n" + "=" * 70)
    print(" " * 20 + "✅ 系统启动完成!")
    print("=" * 70)
    print("\n📌 服务地址:")
    print("  • HTTP 服务器:      http://localhost:8000")
    print("  • WebSocket 服务器: ws://localhost:8765")
    print("  • Live2D 页面:      http://localhost:8000/web_live2d_cubism4.html")
    print("\n📖 使用说明:")
    print("  1. Live2D 页面应该已在浏览器中打开")
    print("  2. 系统正在监听B站直播间弹幕")
    print("  3. 观众发送弹幕后，AI会自动回复并播放语音")
    print("  4. Live2D角色会随语音进行口型同步")
    print("\n💡 提示:")
    print("  • 如需测试，可以运行: python test_local_demo.py")
    print("  • 查看配置文件: config/config.yaml")
    print("  • 按 Ctrl+C 停止所有服务")
    print("\n" + "-" * 70)
    print()


def print_shutdown_info():
    """打印关闭信息"""
    print("\n" + "=" * 70)
    print(" " * 22 + "系统已安全关闭")
    print("=" * 70)
    print("\n感谢使用 AI虚拟主播系统！\n")


def main():
    """主函数"""
    print_banner()
    
    # 检查工作目录
    project_root = Path(__file__).parent
    os.chdir(project_root)
    print(f"📁 工作目录: {project_root}\n")
    
    # 进程管理器
    manager = ProcessManager()
    
    try:
        # 1. 启动 HTTP 服务器
        if not start_http_server(manager):
            print("\n✗ HTTP 服务器启动失败，无法继续")
            return 1
        
        # 2. 启动主程序
        main_program = start_main_program(manager)
        if not main_program:
            print("\n✗ 主程序启动失败")
            manager.terminate_all()
            return 1
        
        # 3. 打开 Live2D 页面
        open_live2d_page()
        
        # 4. 显示启动信息
        print_startup_info()
        
        # 5. 等待用户中断
        try:
            while True:
                # 检查主程序是否还在运行
                if main_program.poll() is not None:
                    print("\n⚠️  主程序已退出")
                    break
                time.sleep(1)
        except KeyboardInterrupt:
            print("\n\n收到停止信号...")
        
    except Exception as e:
        print(f"\n✗ 启动过程中出现错误: {e}")
        import traceback
        traceback.print_exc()
        return 1
        
    finally:
        # 清理所有进程
        manager.terminate_all()
        
        # 额外清理端口（确保彻底释放）
        print("\n正在清理端口...")
        if not is_port_available(8000):
            kill_process_on_port(8000)
        if not is_port_available(8765):
            kill_process_on_port(8765)
        
        print_shutdown_info()
        
    return 0


if __name__ == '__main__':
    exit_code = main()
    sys.exit(exit_code)
